Merged [13770]: Enabled saving ICQ aliases set in Adium to the server. Fixes #466
[adiumx.git] / Gaim Service / adiumGaimEventloop.m
blob5b2055e00332ab1d858824b6fd0f0ff15407eec6
1 /* 
2  * Adium is the legal property of its developers, whose names are listed in the copyright file included
3  * with this source distribution.
4  * 
5  * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
6  * General Public License as published by the Free Software Foundation; either version 2 of the License,
7  * or (at your option) any later version.
8  * 
9  * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
10  * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
11  * Public License for more details.
12  * 
13  * You should have received a copy of the GNU General Public License along with this program; if not,
14  * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
15  */
17 #import "adiumGaimEventloop.h"
18 #import <AIUtilities/CBApplicationAdditions.h>
20 static guint                            sourceId = nil;         //The next source key; continuously incrementing
21 static NSMutableDictionary      *sourceInfoDict = nil;
22 static CFRunLoopRef                     gaimRunLoop = nil;
24 static void socketCallback(CFSocketRef s,
25                            CFSocketCallBackType callbackType,
26                            CFDataRef address,
27                            const void *data,
28                            void *infoVoid);
30  * The sources, keyed by integer key id (wrapped in an NSValue), holding
31  * struct sourceInfo* values (wrapped in an NSValue).
32  */
34 // The structure of values of sourceInfoDict
35 struct SourceInfo {
36     guint tag;
37     CFRunLoopTimerRef timer;
38     CFSocketRef socket;
39     CFRunLoopSourceRef rls;
40     union {
41         GSourceFunc sourceFunction;
42         GaimInputFunction ioFunction;
43     };
44     int fd;
45     gpointer user_data;
48 #pragma mark Remove
50 guint adium_source_remove(guint tag) {
51     struct SourceInfo *sourceInfo = (struct SourceInfo*)
52         [[sourceInfoDict objectForKey:[NSNumber numberWithUnsignedInt:tag]] pointerValue];
53         
54         //      GaimDebug (@"***SOURCE REMOVE : %i",tag);
55     if (sourceInfo){
56                 if (sourceInfo->timer != NULL) { 
57                         //Got a timer; invalidate and release
58                         CFRunLoopTimerInvalidate(sourceInfo->timer);
59                         CFRelease(sourceInfo->timer);
60                         
61                 }else{
62                         //Got a file handle; invalidate and release the source and the socket
63                         CFRunLoopSourceInvalidate(sourceInfo->rls);
64                         CFRelease(sourceInfo->rls);
65                         CFSocketInvalidate(sourceInfo->socket);
66                         CFRelease(sourceInfo->socket);
67                 }
68                 
69                 [sourceInfoDict removeObjectForKey:[NSNumber numberWithUnsignedInt:tag]];
70                 free(sourceInfo);
71                 
72                 return TRUE;
73         }
74         
75         return FALSE;
78 //Like g_source_remove, return TRUE if successful, FALSE if not
79 guint adium_timeout_remove(guint tag) {
80     return (adium_source_remove(tag));
83 #pragma mark Add
85 void callTimerFunc(CFRunLoopTimerRef timer, void *info)
87         struct SourceInfo *sourceInfo = info;
88         
89         //      GaimDebug (@"%x: Fired %f-ms timer (tag %u)",[NSRunLoop currentRunLoop],CFRunLoopTimerGetInterval(timer)*1000,sourceInfo->tag);
90         if (! sourceInfo->sourceFunction(sourceInfo->user_data)) {
91         adium_source_remove(sourceInfo->tag);
92         }
95 guint adium_timeout_add(guint interval, GSourceFunc function, gpointer data)
97         //    GaimDebug (@"%x: New %u-ms timer (tag %u)",[NSRunLoop currentRunLoop], interval, sourceId);
98         
99     struct SourceInfo *info = (struct SourceInfo*)malloc(sizeof(struct SourceInfo));
100         
101         sourceId++;
102         NSTimeInterval intervalInSec = (NSTimeInterval)interval/1000;
103         CFRunLoopTimerContext runLoopTimerContext = { 0, info, NULL, NULL, NULL };
104         CFRunLoopTimerRef runLoopTimer = CFRunLoopTimerCreate(kCFAllocatorDefault, /* default allocator */
105                 (CFAbsoluteTimeGetCurrent() + intervalInSec), /* The time at which the timer should first fire */
106                 intervalInSec, /* firing interval */
107                 0, /* flags, currently ignored */
108                 0, /* order, currently ignored */
109                 callTimerFunc, /* CFRunLoopTimerCallBack callout */
110                 &runLoopTimerContext /* context */
111                 );
113         info->sourceFunction = function;
114         info->timer = runLoopTimer;
115         info->socket = NULL;
116         info->rls = NULL;
117         info->user_data = data;
119         CFRunLoopAddTimer(gaimRunLoop, runLoopTimer, kCFRunLoopCommonModes);
121         NSNumber        *key = [NSNumber numberWithUnsignedInt:sourceId];
122         //Make sure we end up with a valid source id
123         while ([sourceInfoDict objectForKey:key]){
124                 sourceId++;
125                 key = [NSNumber numberWithUnsignedInt:sourceId];
126         }
127         info->tag = sourceId;
129         [sourceInfoDict setObject:[NSValue valueWithPointer:info]
130                                            forKey:key];
132         return sourceId;
135 guint adium_input_add(int fd, GaimInputCondition condition,
136                                           GaimInputFunction func, gpointer user_data)
138     struct SourceInfo *info = (struct SourceInfo*)malloc(sizeof(struct SourceInfo));
139         
140     // Build the CFSocket-style callback flags to use from the gaim ones
141     CFOptionFlags callBackTypes = 0;
142     if ((condition & GAIM_INPUT_READ ) != 0) callBackTypes |= kCFSocketReadCallBack;
143     if ((condition & GAIM_INPUT_WRITE) != 0) callBackTypes |= kCFSocketWriteCallBack;   
144         //      if ((condition & GAIM_INPUT_CONNECT) != 0) callBackTypes |= kCFSocketConnectCallBack;
145         
146     // And likewise the entire CFSocket
147     CFSocketContext context = { 0, info, NULL, NULL, NULL };
148     CFSocketRef socket = CFSocketCreateWithNative(NULL, fd, callBackTypes, socketCallback, &context);
149     NSCAssert(socket != NULL, @"CFSocket creation failed");
150     info->socket = socket;
151         
152     // Re-enable callbacks automatically and _don't_ close the socket on
153     // invalidate
154         CFSocketSetSocketFlags(socket, kCFSocketAutomaticallyReenableReadCallBack | 
155                                                    kCFSocketAutomaticallyReenableDataCallBack |
156                                                    kCFSocketAutomaticallyReenableWriteCallBack);
157         
158     // Add it to our run loop
159     CFRunLoopSourceRef rls = CFSocketCreateRunLoopSource(NULL, socket, 0);
160         
161         CFRunLoopAddSource(gaimRunLoop, rls, kCFRunLoopCommonModes);
162         
163         sourceId++;
164         
165         //      GaimDebug (@"Adding for %i",sourceId);
166         
167         info->rls = rls;
168         info->timer = NULL;
169     info->tag = sourceId;
170     info->ioFunction = func;
171     info->user_data = user_data;
172     info->fd = fd;
173     NSCAssert1([sourceInfoDict objectForKey:[NSNumber numberWithUnsignedInt:sourceId]] == nil, @"Key %u in use", sourceId);
174     [sourceInfoDict setObject:[NSValue valueWithPointer:info]
175                                            forKey:[NSNumber numberWithUnsignedInt:sourceId]];
176         
177     return sourceId;
180 #pragma mark Socket Callback
181 static void socketCallback(CFSocketRef s,
182                                                    CFSocketCallBackType callbackType,
183                                                    CFDataRef address,
184                                                    const void *data,
185                                                    void *infoVoid)
187     struct SourceInfo *sourceInfo = (struct SourceInfo*) infoVoid;
188         
189     GaimInputCondition c = 0;
190     if ((callbackType & kCFSocketReadCallBack) != 0)  c |= GAIM_INPUT_READ;
191     if ((callbackType & kCFSocketWriteCallBack) != 0) c |= GAIM_INPUT_WRITE;
192         //      if ((callbackType & kCFSocketConnectCallBack) != 0) c |= GAIM_INPUT_CONNECT;
193         
194         //      GaimDebug (@"***SOCKETCALLBACK : %i (%i)",info->fd,c);
195         
196         if ((callbackType & kCFSocketConnectCallBack) != 0) {
197                 //Got a file handle; invalidate and release the source and the socket
198                 CFRunLoopSourceInvalidate(sourceInfo->rls);
199                 CFRelease(sourceInfo->rls);
200                 CFSocketInvalidate(sourceInfo->socket);
201                 CFRelease(sourceInfo->socket);
202                 
203                 [sourceInfoDict removeObjectForKey:[NSNumber numberWithUnsignedInt:sourceInfo->tag]];
204                 sourceInfo->ioFunction(sourceInfo->user_data, sourceInfo->fd, c);
205                 free(sourceInfo);
206                 
207         }else{
208                 //              GaimDebug (@"%x: Socket callback: %i",[NSRunLoop currentRunLoop],sourceInfo->tag);
209                 sourceInfo->ioFunction(sourceInfo->user_data, sourceInfo->fd, c);
210         }       
214 static GaimEventLoopUiOps adiumEventLoopUiOps = {
215     adium_timeout_add,
216     adium_timeout_remove,
217     adium_input_add,
218     adium_source_remove
221 GaimEventLoopUiOps *adium_gaim_eventloop_get_ui_ops(void)
223         if(!sourceInfoDict) sourceInfoDict = [[NSMutableDictionary alloc] init];
225         //Determine our run loop
226         gaimRunLoop = CFRunLoopGetCurrent();
227         CFRetain(gaimRunLoop);
228         
229         return &adiumEventLoopUiOps;